/*
   File: SsoFilter.java
   Copyright 2004-2010 99Bill Corporation. All rights reserved.
   Created at   2011-9-8
   Author       zhe.yang
   Changed at   2011-9-8
 */

package com.bill99.golden.inf.sso.filter;

import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import com.bill99.golden.inf.sso.client.IssoClient;
import com.bill99.golden.inf.sso.client.RefreshSSOThread;
import com.bill99.golden.inf.sso.mdp.dto.ValidateTokenRequestDto;
import com.bill99.golden.inf.sso.mdp.dto.ValidateTokenResponseDto;
import com.bill99.golden.inf.sso.mdp.model.SsoTokenInfo;
import com.bill99.golden.inf.sso.service.ClientTokenService;
import com.bill99.golden.inf.sso.service.ILoginOK;
import com.bill99.golden.inf.sso.session.LocalSession;
import com.bill99.golden.inf.sso.util.CM;
import com.bill99.golden.inf.sso.util.SSOUtil;
import com.bill99.golden.inf.sso.util.Statistics;
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;

/**
 * 新的SSO Filter
 */
public class SsoFilter implements Filter {
	private Log log = LogFactory.getLog(this.getClass());

	// 不需要过滤的访问路径
	private String[] notFilterUrls;
	private static String appCode = "SSODefault";
	private static String loginURL;
	private String privilegeFilterBeanName;
	private static boolean isUseLocalTokenBuffer;
	private IssoClient ssoClient;
	private String[] defaultPrivileges;
	// private String appId;
	private IPrivilegeFilter privilegeFilter;
	private static boolean isAdminHasAllPrivilege = false;
	private ILoginOK loginOKFunction;
	private IVisitFilter visitFilter;

	public void destroy() {
		notFilterUrls = null;
	}

	public void doFilter(ServletRequest servletRequest,
			ServletResponse servletResponse, FilterChain filterChain)
			throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) servletRequest;
		HttpServletResponse response = (HttpServletResponse) servletResponse;
		boolean isSkip = checkSkipFilter(request);
		// System.out.println(" isSkip=" + isSkip);

		if (isSkip == false) {
			long st = System.currentTimeMillis();
			String token = SSOUtil.getToken(request);
			log.debug("获取token：sso token=" + token);
			if (SSOUtil.isValidateToken(token) == false) {
				log.info("SSO登录验证失败，请重新登录！");
				response.sendRedirect(this.getWholeUrl(request, loginURL));
				return;
			}
			ValidateTokenResponseDto ssoInfo = null;
			if (isUseLocalTokenBuffer) {
				ssoInfo = this.validateSSOFromBuffer(request, token);
				if (ssoInfo != null && ssoInfo.isValid() == false) {
					log.info("SSO登录验证失败，请重新登录！");
					response.sendRedirect(this.getWholeUrl(request, loginURL));
					return;
				} else if (ssoInfo != null && ssoInfo.isValid() == true) {
					log.debug("validate from local buffer:OK..........");
					RefreshSSOThread.refreshSSOTime(this.ssoClient, request,
							token, appCode);
				}
			}
			if (ssoInfo == null) {
				log.debug("validate from CAS..........");
				ssoInfo = ssoClient.validateSSO(request, token, appCode);
			}
			log.debug("验证SSO登录结果：ssoInfo:" + ssoInfo);
			long ed = System.currentTimeMillis();
			Statistics.addValidateLog(ed - st);
			if (visitFilter != null) {
				this.visitFilter.onVisitProcess(ssoInfo, request, response);
			}
			String defaultUrl = null;
			LocalSession lss = SSOUtil.getLocalSession(request);
			System.out.println("\r\n\r\n");
			System.out.println("ssoInfo=" + ssoInfo);
			if (ssoInfo != null
					&& (ssoInfo.isValid() || SsoTokenInfo.ERROR_CODE_TOKEN_TIMEOUTED
							.equals(ssoInfo.getErrorCode()))) {
				String ssoFlag = request.getParameter("ssoFlag");
				if ("defaultUrl".equals(ssoFlag)) {
					String str = request.getRequestURL().toString();
					String qstr = request.getQueryString();
					String url = str;
					if (CM.stringIsNotEmpty(qstr))
						url += "?" + qstr;
					defaultUrl = url;
					System.out.println("url=" + defaultUrl);
					lss.setAttribute("#SSO%_defaultUrl", url);
				}
			} else {
				defaultUrl = (String) lss.getAttribute("#SSO%_defaultUrl");
			}
			if (ssoInfo != null && ssoInfo.isValid()) {
				ClientTokenService.bufferTokenInfo(token, ssoInfo);
				request.getSession(true).setAttribute(IssoClient.SSO_INFO_NAME,
						ssoInfo);
				if (this.privilegeFilterBeanName != null
						&& this.privilegeFilterBeanName.trim().length() > 0) {
					boolean needCheckPrivilege = true;
					if (this.checkSkipPrivilege(request))
						needCheckPrivilege = false;
					else {
						if ("admin".equals(ssoInfo.getUserCode())) {
							if (isAdminHasAllPrivilege)
								needCheckPrivilege = false;
						}
					}
					if (needCheckPrivilege) {
						if (privilegeFilter != null) {
							String url = this.getVisitUrl(request, true);
							String userId = ssoInfo.getUserId();
							if (privilegeFilter.hasPrivilege(url, userId) == false) {
								log.info("权限验证失败！visitUrl=" + url
										+ " userCode=" + ssoInfo.getUserCode()
										+ " userName=" + ssoInfo.getUserName());
								String urlPage = privilegeFilter
										.getAccessDeniedPage()
										+ "?accessUrl="
										+ Base64.encode(url.getBytes("UTF-8"));
								urlPage = this.getWholeUrl(request, urlPage);
								response.sendRedirect(urlPage);
								return;
							}
						}
					}
				}

			} else {
				log.info("SSO登录验证失败，请重新登录！");
				if (ssoInfo != null
						&& SsoTokenInfo.ERROR_CODE_TOKEN_TIMEOUTED
								.equals(ssoInfo.getErrorCode())) {
					System.out
							.println("sessionTimeOut:--------------------------");
					// 如果是由于超时的原因，则记住当前的页面重新登录
					String newUrl = this.getWholeUrl(request, loginURL);
					if (CM.stringIsNotEmpty(defaultUrl)) {
						String ck = java.net.URLEncoder.encode(defaultUrl,
								"UTF-8");
						if (newUrl.indexOf("?") > 0) {
							newUrl += "&cmd=gotoDefault&dfUrl=" + ck;
						} else {
							newUrl += "?cmd=gotoDefault&dfUrl=" + ck;
						}
					}
					System.out.println("redirectUrl=" + newUrl);
					response.sendRedirect(newUrl);
					return;
				} else {
					response.sendRedirect(this.getWholeUrl(request, loginURL));
					return;
				}
			}

			// 登录成功，调用ILoginOK接口
			if (this.loginOKFunction != null) {
				this.loginOKFunction.afterLoginOK(request, ssoInfo);
			}
		}

		filterChain.doFilter(servletRequest, servletResponse);
	}

	private ValidateTokenResponseDto validateSSOFromBuffer(
			HttpServletRequest request, String token) {
		String explorerType = request.getHeader("user-agent");
		String userIp = request.getRemoteHost();
		ValidateTokenRequestDto r = new ValidateTokenRequestDto();
		r.setAppCode(appCode);
		r.setUserIp(userIp);
		r.setExplorerType(explorerType);
		r.setToken(token);
		r.setVisitUrl("");
		r.setValidatePrivilege(false);
		ValidateTokenResponseDto response = ClientTokenService
				.validateTokenFromBuffer(r);
		return response;
	}

	private String getWholeUrl(HttpServletRequest req, String url) {
		if (url == null)
			return null;
		if (url.startsWith("http://") || url.startsWith("https://"))
			return url;
		String ctxPath = req.getContextPath();
		if (ctxPath.startsWith("/")) {
			ctxPath = ctxPath.substring(1);
		}
		if (ctxPath.endsWith("/")) {
			ctxPath = ctxPath.substring(0, ctxPath.length() - 1);
		}
		if (url.indexOf("/" + ctxPath + "/") >= 0) {
			return url;
		} else {
			if (url.startsWith("/")) {
				url = "/" + ctxPath + url;
			} else {
				url = "/" + ctxPath + "/" + url;
			}
			return url;
		}
	}

	private String getVisitUrl(HttpServletRequest req, boolean checkMethod) {
		String sURI = req.getRequestURI();
		String ctxpath = req.getContextPath();
		if (ctxpath.length() > 0 && ctxpath.charAt(ctxpath.length() - 1) == '/') {
			ctxpath = ctxpath.substring(0, ctxpath.length() - 1);
		}
		if (ctxpath.length() > 0 && sURI.indexOf(ctxpath) == 0) {
			sURI = sURI.substring(ctxpath.length());
		}
		String qstr = req.getQueryString();
		if (qstr != null && qstr.trim().length() > 0) {
			sURI += "?" + qstr;
		}
		if (checkMethod) {
			qstr = req.getQueryString();
			if (qstr != null && qstr.indexOf("method=") >= 0) {
				return sURI;
			}
			String method = req.getParameter("method");
			if (method != null && !method.trim().equals("")) {
				if (sURI.indexOf("?") > 0)
					sURI = sURI + "&method=" + method;
				else
					sURI = sURI + "?method=" + method;
			}
		}
		return sURI;
	}

	public void init(FilterConfig filterConfig) throws ServletException {
		String notFilterFiles = filterConfig.getInitParameter("notFilterFiles");
		if (notFilterFiles != null && !notFilterFiles.trim().equals("")) {
			notFilterUrls = notFilterFiles.split(",");
		} else {
			notFilterUrls = new String[] {};
		}
		for (int i = 0; i < notFilterUrls.length; i++) {
			notFilterUrls[i] = notFilterUrls[i].trim();
		}
		String dpStr = filterConfig.getInitParameter("defaultPrivileges");
		if (dpStr != null && !dpStr.trim().equals("")) {
			this.defaultPrivileges = dpStr.split(",");
		} else {
			this.defaultPrivileges = new String[] {};
		}
		for (int i = 0; i < notFilterUrls.length; i++) {
			notFilterUrls[i] = notFilterUrls[i].trim();
		}
		for (int i = 0; i < defaultPrivileges.length; i++) {
			defaultPrivileges[i] = defaultPrivileges[i].trim();
		}
		appCode = filterConfig.getInitParameter("appCode");
		loginURL = filterConfig.getInitParameter("loginURL");
		isUseLocalTokenBuffer = "true".equalsIgnoreCase(filterConfig
				.getInitParameter("useLocalTokenBuffer"))
				|| "yes".equalsIgnoreCase(filterConfig
						.getInitParameter("useLocalTokenBuffer"));
		this.privilegeFilterBeanName = filterConfig
				.getInitParameter("privilegeFilterBeanName");
		// this.appId = filterConfig.getInitParameter("appId");
		isAdminHasAllPrivilege = "true".equalsIgnoreCase(filterConfig
				.getInitParameter("AdminHasAllPrivilege"))
				|| "yes".equalsIgnoreCase(filterConfig
						.getInitParameter("AdminHasAllPrivilege"));

		String afterLoginOKClass = filterConfig
				.getInitParameter("afterLoginOKClass");
		String visitFilterClass = filterConfig
				.getInitParameter("visitFilterClass");
		if (CM.stringIsNotEmpty(afterLoginOKClass)) {
			try {
				Class cls = Class.forName(afterLoginOKClass);
				this.loginOKFunction = (ILoginOK) cls.newInstance();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				this.loginOKFunction = null;
				e.printStackTrace();
			}
		}
		if (CM.stringIsNotEmpty(visitFilterClass)) {
			try {
				Class cls = Class.forName(visitFilterClass);
				this.visitFilter = (IVisitFilter) cls.newInstance();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				this.visitFilter = null;
				e.printStackTrace();
			}
		}

		System.out.println("!!!!!SsoFilter Init..............................");

		try {
			this.ssoClient = SSOUtil.getIssoClient(filterConfig
					.getServletContext());
			if (CM.stringIsNotEmpty(this.privilegeFilterBeanName)) {
				ApplicationContext context = WebApplicationContextUtils
						.getRequiredWebApplicationContext(filterConfig
								.getServletContext());
				this.privilegeFilter = (IPrivilegeFilter) context
						.getBean(this.privilegeFilterBeanName);
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			log.error("初始化Bean配置s错误！", e);
			e.printStackTrace();
		}
	}

	private boolean checkSkipFilter(HttpServletRequest req) {
		if (notFilterUrls == null || notFilterUrls.length <= 0) {
			return false;
		}
		String sURI = this.getVisitUrl(req, false);
		String all = "[a-zA-Z0-9_./?=&\\\\\\-%$@#]*";
		// System.out.print("URL:" + sURI);
		for (int i = 0; i < notFilterUrls.length; i++) {
			String urlStr = notFilterUrls[i];
			if (urlStr == null || urlStr.length() < 1)
				continue;
			String regex = urlStr;
			regex = regex.replace("\\", "\\\\");
			regex = regex.replace("*", all);
			Pattern p = Pattern.compile(regex);
			String input = sURI;
			Matcher m = p.matcher(input);
			if (m.find()) {
				int st = m.start();
				int end = m.end();
				if (st == 0 && end == input.length()) {
					return true;
				}
			}
		}
		return false;
	}

	private boolean checkSkipPrivilege(HttpServletRequest req) {
		if (notFilterUrls == null || notFilterUrls.length <= 0) {
			return false;
		}
		String sURI = this.getVisitUrl(req, true);
		String all = "[a-zA-Z0-9_./?=&\\\\\\-%$@#]*";
		// System.out.print("checkSkipPrivilege====URL:" + sURI);
		for (int i = 0; i < this.defaultPrivileges.length; i++) {
			String urlStr = defaultPrivileges[i];
			if (urlStr == null || urlStr.length() < 1)
				continue;
			String regex = urlStr;
			regex = regex.replace("\\", "\\\\");
			regex = regex.replace("*", all);
			Pattern p = Pattern.compile(regex);
			String input = sURI;
			Matcher m = p.matcher(input);
			if (m.find()) {
				int st = m.start();
				int end = m.end();
				if (st == 0 && end == input.length()) {
					return true;
				}
			}
		}
		return false;
	}

	public static String getAppCode() {
		return appCode;
	}

	public static String getLoginURL() {
		return loginURL;
	}

	public static boolean isUseLocalTokenBuffer() {
		return isUseLocalTokenBuffer;
	}

}
